import { AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ContentChildren, ElementRef, EmbeddedViewRef, EventEmitter, forwardRef, Inject, Input, OnDestroy, Output, QueryList, TemplateRef, ViewChild, ViewContainerRef } from '@angular/core';
import { Observable, Subject } from 'rxjs';


let idx: number = 0;

@Component({
    selector: 'myTabPanel',
    template: `
        <div [attr.id]="id" class="p-tabview-panel" [hidden]="!selected"
            role="tabpanel" [attr.aria-hidden]="!selected" *ngIf="!closed">
            <ng-content></ng-content>
        </div>
    `,
    styleUrls: ['./tabview.component.css']
})
export class MyTabPanel implements AfterContentInit, OnDestroy {

    @Input() closable: boolean;

    @Input() isHome: boolean;

    @Input() headerStyle: any;

    @Input() headerStyleClass: string;

    @Input() cache: boolean = true;

    @Input() tooltip: any;

    @Input() tooltipPosition: string = 'top';

    @Input() tooltipPositionStyle: string = 'absolute';

    @Input() tooltipStyleClass: string;

    @Output() onContextMenu: EventEmitter<MouseEvent> = new EventEmitter();

    closed: boolean;

    view: EmbeddedViewRef<any>;

    _selected: boolean;

    _disabled: boolean;

    _header: string | TemplateRef<any>;

    _leftIcon: string;

    _rightIcon: string;

    loaded: boolean;

    id: string = `p-tabpanel-${idx++}`;

    tabView: MyTabView;

    active = new Subject<void>()
    get active$(): Observable<void> { return this.active }

    constructor(@Inject(forwardRef(() => MyTabView)) tabView, public viewContainer: ViewContainerRef, public cd: ChangeDetectorRef) {
        this.tabView = tabView as MyTabView;
    }

    ngAfterContentInit() {
    }

    @Input() get selected(): boolean {
        return this._selected;
    }

    set selected(val: boolean) {
        this._selected = val;

        if (val) {
            this.active.next();
        }
        if (!this.loaded) {
            this.cd.detectChanges();
        }

        if (val)
            this.loaded = true;
    }

    @Input() get disabled(): boolean {
        return this._disabled;
    };

    set disabled(disabled: boolean) {
        this._disabled = disabled;
        this.tabView.cd.markForCheck();
    }

    @Input() get header(): string | TemplateRef<any> {
        return this._header;
    }

    set header(header: string | TemplateRef<any>) {
        this._header = header;
        this.tabView.cd.markForCheck();
    }

    @Input() get leftIcon(): string {
        return this._leftIcon;
    }

    set leftIcon(leftIcon: string) {
        this._leftIcon = leftIcon;
        this.tabView.cd.markForCheck();
    }

    @Input() get rightIcon(): string {
        return this._rightIcon;
    }

    set rightIcon(rightIcon: string) {
        this._rightIcon = rightIcon;
        this.tabView.cd.markForCheck();
    }

    ngOnDestroy() {
        this.view = null;
    }
}

@Component({
    selector: 'myTabView',
    templateUrl: './tabview.component.html',
    changeDetection: ChangeDetectionStrategy.OnPush,
    /*encapsulation: ViewEncapsulation.None,*/
    styleUrls: ['./tabview.component.css']
})
export class MyTabView implements AfterContentInit, AfterViewChecked, AfterViewInit {

    @Input() showAdd: boolean = false;

    @Input() navWrap: boolean = false;

    @Input() orientation: string = 'top';

    @Input() style: any;

    @Input() styleClass: string;

    @Input() controlClose: boolean;

    @Input() extraContent: TemplateRef<any>;

    @ViewChild('navbar') navbar: ElementRef;

    @ContentChildren(MyTabPanel) tabPanels: QueryList<MyTabPanel>;

    @Output() onChange: EventEmitter<any> = new EventEmitter();

    @Output() onClose: EventEmitter<any> = new EventEmitter();

    @Output() onAddTab: EventEmitter<any> = new EventEmitter();

    @Output() onDragDrop: EventEmitter<any> = new EventEmitter();

    @Output() activeIndexChange: EventEmitter<number> = new EventEmitter();

    initialized: boolean = false;

    tabs: MyTabPanel[];

    _activeIndex: number = 0;
    tabHome: MyTabPanel = null;
    translateX: number = 0;
    leftDisabled = true;
    rightDisabled = true;

    preventActiveIndexPropagation: boolean;

    tabChanged: boolean;

    constructor(public el: ElementRef, public cd: ChangeDetectorRef) { }

    ngAfterContentInit() {
        this.initTabs();

        this.tabPanels.changes.subscribe(_ => {
            this.initTabs();
        });
    }

    ngAfterViewInit() {
        let navTablist = this.findSingle(this.navbar.nativeElement, '.p-tabview-nav-tablist');
        navTablist.addEventListener('mousewheel', (event) => {
            if (event.wheelDelta > 0) {
                navTablist.scrollLeft -= 100;
            } else {
                navTablist.scrollLeft += 100;
            }
        })
    }
    ngAfterViewChecked() {
        if (this.tabChanged) {
            this.tabChanged = false;
        }
    }

    isString(val) { return typeof val === 'string'; }
    initTabs(): void {
        let index = this.activeIndex;

        this.tabs = this.tabPanels.toArray();
        this.tabHome = this.findHomeTab();
        this.setNavWidth();
        let selectedTab: MyTabPanel = this.findSelectedTab();
        if (this.deferIndex != -1) {
            if (this.deferIndex < this.tabs.length)
                index = this.deferIndex;
            this.deferIndex = -1;
        } else {
            if (selectedTab) {
                index = this.findTabIndex(selectedTab);
            } else if (!selectedTab && this.tabs.length) {
                //if (this.tabHome) this.tabHome.selected = true;
                if (index == null) {
                    index = 0;
                } else if (this.tabs.length <= this.activeIndex) {
                    index = this.tabs.length - 1;
                }
                this.tabChanged = true;
            }
        }
        //add setTimeout for ExpressionChangedAfterItHasBeenCheckedError
        setTimeout(() => {
            this.activeIndex = index;
            this.cd.markForCheck();
        })
    }
    onTabContextMenu(event: MouseEvent, tab: MyTabPanel) {
        tab.onContextMenu.emit(event);
    }
    open(event: Event, tab: MyTabPanel) {
        if (tab.disabled) {
            if (event) {
                event.preventDefault();
            }
            return;
        }

        if (!tab.selected) {
            let selectedTab: MyTabPanel = this.findSelectedTab();
            if (selectedTab) {
                selectedTab.selected = false
            }

            this.tabChanged = true;
            tab.selected = true;
            let selectedTabIndex = this.findTabIndex(tab);
            this.preventActiveIndexPropagation = true;
            this.activeIndexChange.emit(selectedTabIndex);
            this.onChange.emit({ originalEvent: event, index: selectedTabIndex });

            this.makeActiveTabVisible(selectedTabIndex);
        }

        if (event) {
            event.preventDefault();
        }
    }

    close(event: Event, tab: MyTabPanel) {
        if (this.controlClose) {
            this.onClose.emit({
                originalEvent: event,
                index: this.findTabIndex(tab),
                close: () => {
                    this.closeTab(tab);
                }
            }
            );
        }
        else {
            this.closeTab(tab);
            this.onClose.emit({
                originalEvent: event,
                index: this.findTabIndex(tab)
            });
        }

        event.stopPropagation();
    }
    addTab(event: Event) {
        this.onAddTab.emit(event);
    }
    closeTab(tab: MyTabPanel) {
        if (tab.disabled) {
            return;
        }
        if (tab.selected) {
            this.tabChanged = true;
            tab.selected = false;
            /*for(let i = 0; i < this.tabs.length; i++) {
                let tabPanel = this.tabs[i];
                if (!tabPanel.closed&&!tab.disabled) {
                    tabPanel.selected = true;
                    break;
                }
            }*/
            let i = this.findTabIndex(tab);
            if (i > 1) i--;
            else if (i == this.tabs.length - 1) i = 0;
            else i++;
            let tabPanel = this.tabs[i];
            tabPanel.selected = true;
        }

        tab.closed = true;
    }

    findSelectedTab() {
        for (let i = 0; i < this.tabs.length; i++) {
            if (this.tabs[i].selected) {
                return this.tabs[i];
            }
        }
        return null;
    }
    findHomeTab() {
        for (let i = 0; i < this.tabs.length; i++) {
            if (this.tabs[i].isHome) {
                return this.tabs[i];
            }
        }
        return null;
    }

    findTabIndex(tab: MyTabPanel) {
        let index = -1;
        for (let i = 0; i < this.tabs.length; i++) {
            if (this.tabs[i] == tab) {
                index = i;
                break;
            }
        }
        return index;
    }

    getBlockableElement(): HTMLElement {
        return this.el.nativeElement.children[0];
    }

    @Input() get activeIndex(): number {
        return this._activeIndex;
    }
    deferIndex: number = -1;
    set activeIndex(val: number) {
        //if (this.preventActiveIndexPropagation) {
        //    this.preventActiveIndexPropagation = false;
        //    return;
        //}

        if (this.tabs && this.tabs.length && this._activeIndex != null && this.tabs.length > val) {
            this._activeIndex = val;
            //this.findSelectedTab().selected = false;
            for (let i = 0; i < this.tabs.length; i++) {
                this.tabs[i].selected = false;
            }
            this.tabs[this._activeIndex].selected = true;
            this.makeActiveTabVisible(val);
        } else {
            this.deferIndex = val;
        }
    }

    findSingle(element: any, selector: string): any {
        if (element) {
            return element.querySelector(selector);
        }
        return null;
    }
    find(element: any, selector: string): any[] {
        return Array.from(element.querySelectorAll(selector));
    }
    getWidth(el): number {
        let width = el.offsetWidth;
        let style = getComputedStyle(el);

        width -= parseFloat(style.paddingLeft) + parseFloat(style.paddingRight) + parseFloat(style.borderLeftWidth) + parseFloat(style.borderRightWidth);

        return width;
    }
    setNavWidth() {
        if (!this.navbar) return;
        this.initialized = true;
        let navHome = this.findSingle(this.navbar.nativeElement, '.p-tabview-nav-home');
        let navAdd = this.findSingle(this.navbar.nativeElement, '.p-tabview-nav-add');
        let navExtra = this.findSingle(this.navbar.nativeElement, '.p-tabview-nav-extra');
        let navTablist = this.findSingle(this.navbar.nativeElement, '.p-tabview-nav-tablist');
        let navGroup = this.findSingle(this.navbar.nativeElement, '.p-tabview-nav-group');

        let width1 = navExtra ? this.getWidth(navExtra) : 0;
        let width2 = this.getWidth(navHome) + this.getWidth(navAdd);

        navGroup.style['max-width'] = `calc(100% - ${width1}px)`;
        navTablist.style['max-width'] = `calc(100% - ${width2}px)`;
    }
    makeActiveTabVisible(i) {
        if (!this.navbar) return;
        let navTablist = this.findSingle(this.navbar.nativeElement, '.p-tabview-nav-tablist');
        let navList = this.find(this.navbar.nativeElement, '.p-tabview-nav-tablist li')

        if (this.tabHome) i -= 1;
        if (i < 0 || i >= navList.length) return;
        if (navList[i].offsetLeft + navList[i].offsetWidth - navTablist.scrollLeft > navTablist.offsetWidth) {
            let x = navList[i].offsetLeft + navList[i].offsetWidth - navTablist.offsetWidth;
            if (i < navList.length - 1) x += 40;
            navTablist.scrollTo({
                left: x,
                behavior: 'smooth'
            })
            return;
        }
        if (navList[i].offsetLeft < navTablist.scrollLeft) {
            let x = navList[i].offsetLeft;
            if (i > 0) x -= 40;
            navTablist.scrollTo({
                left: x,
                behavior: 'smooth'
            })
            return;
        }
    }
    dragDrop(event: any) {
        this.onDragDrop.emit(event);
    }
}

